Restore thread panel per channel across switches - #5593
Open
aparisesquare wants to merge 1 commit into
Open
Conversation
Leaving a channel and returning now restores the thread panel the way it was left: open on the same thread, or closed. Panel state stays URL-backed (?thread=<headId>); the change adds a session-scoped per-channel memory that seeds the URL goChannel builds. - channelPanelMemory.ts: tri-state per-channel memory (thread id / closed / never visited), sessionStorage-backed like the thread-panel width, so it survives a reload but not an app restart. - useChannelThreadMemory.ts: continuously mirrors the raw ?thread value in ChannelScreen; skips huddle transcripts and the forum-post view. A thread snap-closed by useThreadTargetSync records null, so stale entries are forgotten instead of retried. - goChannel: navigations with no explicit target (thread/messageId/ agentSession/autoSend) seed thread from memory — the URL is right the first time, so a switch stays one history entry and back/forward keeps its per-entry params. Restored threads whose head paged out of the initial window ride the existing deep-link splice (targetThreadRootId <- search.thread). - resetChannelPanelMemory() wired into resetCommunityState() and added to the AGENTS.md singleton list — channel ids are community-local. Covered by channelPanelMemory.test.mjs and a Playwright spec exercising restore, per-channel isolation, and closed-stays-closed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Anthony Parise <aparise@squareup.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Leaving a channel and returning now restores the thread panel the way it was left — open on the same thread, or closed. Previously every channel switch closed the panel: sidebar navigation builds a fresh
/channels/$channelIdURL with nothreadsearch param, so the URL-backed panel state was lost on every forward navigation (back/forward already restored it per history entry).How
Panel state stays URL-backed; the change adds a session-scoped per-channel memory that seeds the URL
goChannelbuilds. Four small pieces, no relay/schema changes:channelPanelMemory.ts(new): tri-state per-channel memory — thread head id ("open on this thread"),null("user left it closed", so a closed panel stays closed on return), no entry ("never visited"). Backed by sessionStorage following the thread-panel width precedent (useThreadPanelWidth): survives a reload, dies with the app session.useChannelThreadMemory.ts(new): continuously mirrors the raw?threadvalue into the memory from ChannelScreen (one call line — the file sits near the 1000-line ceiling). Skips huddle transcripts (useHuddleThreadIsolationforce-closes threads there) and the forum-post view. Continuous recording makes every exit path correct for free, and turns a thread snap-closed byuseThreadTargetSync(deleted/unfetchable head) intomemory = null— forgotten, never retried, so no restore loop is possible.goChannel: navigations that carry no explicit target (thread/messageId/agentSession/autoSend) seedthreadfrom memory when building the search params. One choke point covers sidebar, tray, notifications, and DM opens; the URL is right the first time, so a switch stays exactly one history entry, and explicit targets plus back/forward win by construction. A restored thread whose head paged out of the initial window rides the existing deep-link splice (targetThreadRootId ← search.threadinchannels.$channelId.tsx→ChannelRouteScreenfetch).resetChannelPanelMemory()wired intoresetCommunityState()and added to the AGENTS.md singleton list — channel ids are community-local, so remembered panel state must not leak across a community switch (memory + its sessionStorage key are both cleared).Not restored (out of scope): in-panel expanded replies and scroll position (plain React state); profile/agent-session panels (the memory value can grow to a partial record of those keys later without moving any wiring).
Testing
channelPanelMemory.test.mjs— tri-state round-trip, per-channel isolation, sessionStorage write-through, redundant-write skip, reset semantics, storage-failure resilience (7 tests).tests/e2e/thread-panel-persistence.spec.ts(registered in the smoke project) — open thread in #general → switch to #random (panel closed there) → return (panel restored, same thread, URL carriesthread=) → close panel → leave and return (stays closed).biome check,tsc --noEmit, andcheck-file-sizesclean.